package com.siaron.framework.eventbus;

import com.siaron.framework.eventbus.annotation.ConditionalOnEventBusEnabled;
import com.siaron.framework.eventbus.autoconfigure.BindingCache;
import com.siaron.framework.eventbus.model.FunctionInfo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;

import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author xielongwang
 * @create 2021/7/19 5:29 下午
 * @email siaron.wang@gmail.com
 * @description 注册binder的binding配置
 */
public class EventBusEnvironmentPostProcessor implements EnvironmentPostProcessor {

    public static final String DEFAULTS_PROPERTY_SOURCE_NAME = "eventBusOverridesBinderProperties";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        if (environment.containsProperty(ConditionalOnEventBusEnabled.EVENT_BUS_ENABLED)) {
            if (Boolean.FALSE.toString()
                    .equalsIgnoreCase(environment.getProperty(ConditionalOnEventBusEnabled.EVENT_BUS_ENABLED))) {
                return;
            }
        }
        Map<String, FunctionInfo> fun = BindingCache.getFun();
        if (CollectionUtils.isEmpty(fun)) {
            return;
        }

        //针对binder实现属性注入.合并多个map成一个map
        Map<String, Object> defaults = fun.values().stream()
                .map(FunctionInfo::getBinderBindings)
                .map(Map::entrySet)
                .flatMap(Set::stream)
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (l, r) -> r));
        if (CollectionUtils.isEmpty(defaults)) {
            return;
        }

        addOrReplace(environment.getPropertySources(), defaults, DEFAULTS_PROPERTY_SOURCE_NAME, false);
    }

    public static void addOrReplace(MutablePropertySources propertySources, Map<String, Object> map,
                                    String propertySourceName, boolean first) {
        MapPropertySource target = null;
        if (propertySources.contains(propertySourceName)) {
            PropertySource<?> source = propertySources.get(propertySourceName);
            if (source instanceof MapPropertySource) {
                target = (MapPropertySource) source;
                for (String key : map.keySet()) {
                    if (!target.containsProperty(key)) {
                        target.getSource().put(key, map.get(key));
                    }
                }
            }
        }
        if (target == null) {
            target = new MapPropertySource(propertySourceName, map);
        }
        if (!propertySources.contains(propertySourceName)) {
            if (first) {
                propertySources.addFirst(target);
            } else {
                propertySources.addLast(target);
            }
        }
    }
}
